package uk.org.fuzelogik.eldaw.auth.openid;

import javax.servlet.http.HttpServletRequest;

import org.apache.wicket.Application;
import org.apache.wicket.protocol.http.WebRequest;
import org.openid4java.association.AssociationException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.MessageException;
import org.openid4java.message.ParameterList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import uk.org.fuzelogik.eldaw.auth.DelegatedAuthenticationReturnPage;
import uk.org.fuzelogik.eldaw.auth.IDelegatedAuthenticationStrategy;
import uk.org.fuzelogik.eldaw.wicket.component.IncompatibleApplicationException;


/**
 * A page that gets redirected to from an OpenID identity provider. After a user
 * has authenticated with an identity provider, they get redirected to this
 * page. As such, <strong>this page must be mounted with the
 * <code>QueryStringUrlCodingStrategy</code></strong>. The <em>absolute
 * mountpoint</em>
 * (i.e. full URL) of where this page is mounted to should be returned by your
 * application's implementation of
 * {@link IOpenIDAwareApplication#getOpenIdReturnUrl()}.
 * 
 * @author cgdavies
 */
public abstract class OpenIDReturnPage extends
	DelegatedAuthenticationReturnPage
{
	final private static Logger LOG = LoggerFactory
		.getLogger( OpenIDReturnPage.class );

	private IOpenIDAwareApplication getOpenIdAwareApplication()
	{
		Application app = getApplication();

		if( !( app instanceof IOpenIDAwareApplication ) )
			throw new IncompatibleApplicationException(
				IOpenIDAwareApplication.class );

		return (IOpenIDAwareApplication)app;
	}

	final protected String getAuthenticatedIdentifier(
		IDelegatedAuthenticationStrategy strategy )
		throws MalformedRequestException
	{
		OpenIDAuthenticationStrategy openIdStrategy = (OpenIDAuthenticationStrategy)strategy;
		DiscoveryInformation di = openIdStrategy.getDiscoveryInformation();

		// Build the request URL.
		HttpServletRequest req = ( (WebRequest)getRequest() )
			.getHttpServletRequest();
		StringBuffer requestUrl = req.getRequestURL();
		String queryString = req.getQueryString();

		if( ( queryString != null ) && ( queryString.length() > 0 ) )
			requestUrl.append( "?" ).append( queryString );

		// Validate the response.
		IOpenIDAwareApplication app = getOpenIdAwareApplication();
		ConsumerManager consumerManager = app.getConsumerManager();

		ParameterList openIdResp = new ParameterList( req.getParameterMap() );

		try
		{
			VerificationResult result = consumerManager.verify( requestUrl
				.toString(), openIdResp, di );

			Identifier id = result.getVerifiedId();

			if( id == null )
				return null;
			else
				return id.getIdentifier();
		}
		catch( DiscoveryException ex )
		{
			LOG.error(
				"DiscoveryException trying to verify authentication.",
				ex );
			return null;
		}
		catch( MessageException ex )
		{
			LOG.error( "MessageException trying to verify authentication.", ex );
			return null;
		}
		catch( AssociationException ex )
		{
			LOG.error(
				"AssociationException trying to verify authentication.",
				ex );
			return null;
		}
	}

	final protected boolean isAuthenticationStrategyCompatible(
		IDelegatedAuthenticationStrategy strategy )
	{
		return ( strategy instanceof OpenIDAuthenticationStrategy );
	}
}
